#include <string>
#include <sstream>
#include <cstdlib>
#include <ctime>
#include <CGAL/Timer.h>

#include <Geex/graphics/geexapp.h>
#include <Geex/basics/file_system.h>
extern "C"{
#include <Geex/third_party/misc/gl2ps.h>
}
#include <glut_viewer/tweak_bar.h>

#include "packing_aggregator.h"

namespace Packing {
		
	using namespace Geex;

	void TW_CALL tw_lloyd(void *clientData);
	void TW_CALL tw_shrink(void *clientData);
	void TW_CALL tw_pack(void*);
	void TW_CALL tw_swap(void*);
	void TW_CALL tw_sa_pack(void*);
	void TW_CALL tw_print_pdf(void*);
	void TW_CALL tw_optimize(void*);
	void tw_redraw();
	//void TW_CALL tw_feature_set_callback(const void*, void *);
	//void TW_CALL tw_feature_get_callback(void*, void*);
	//void TW_CALL tw_curvature_get_callback(void*, void*);
	//void TW_CALL tw_curvature_set_callback(const void*, void*);

	
    class Packing_application : public GeexApp 
	{
    public:
        Packing_application(int argc, char** argv) : GeexApp(argc, argv) 
		{ 
            hdr_ = false ;
  
           // get_arg("nb_iter", nb_iter_) ;
			polytope_filename = get_file_arg("txt");
			if (polytope_filename.empty())
				polytope_filename = get_file_arg("poly");
			nb_iter = 10;
			coverage_rate = -1.0;
			get_arg("coverage", coverage_rate);
			if (coverage_rate <= 0.0 || coverage_rate >= 1.0)
				coverage_rate = 0.7;

			// set the window title for clarity
			std::string title(polytope_filename);
			int i ;
			for (i = title.length()-1; i >=0; i--)
			{
				if (title[i] == '\\' || title[i] == '/')
					break;
			}
			title.assign(title.substr(i+1, title.npos));
			title.append(" coverage=");
			std::ostringstream oss;
			oss << coverage_rate;
			title.append(oss.str());
			char *ctitle = new char[title.length()+1];
			std::strcpy(ctitle, title.c_str());
			ctitle[title.length()] = '\0';
			glut_viewer_set_window_title(ctitle);
			//glut_viewer_set_window_title("test");
		}

        Packing_aggregator* Packing_context() { return static_cast<Packing_aggregator*>(scene()) ; }

        void init_scene() 
		{
			scene_ = new Packing_aggregator(polytope_filename, coverage_rate);
        }

		void lloyd()
		{
			for (unsigned int i = 0; i < nb_iter; i++)
			{
				Packing_context()->lloyd(1, tw_redraw);
				//Packing_context()->redraw();
				//glut_viewer_redraw();
			}
		}
		void optimize()
		{
			Packing_context()->optimize();
			glut_viewer_redraw();
		}
		void shrink()
		{
			Packing_context()->shrink();
			Packing_context()->redraw();
			glut_viewer_redraw();
		}
		void redraw()
		{
			Packing_context()->redraw();
			glut_viewer_redraw();
		}
		void pack()
		{
			Packing_context()->pack(tw_redraw);
		}
		void sa_pack()
		{
			Packing_context()->sa_pack(tw_redraw);
		}
		void swap()
		{
			Packing_context()->swap();
			redraw();
		}
        void init_gui() 
		{
            GeexApp::init_gui() ;
			
            TwBar* graphics_bar = TwNewBar("Graphics");
			TwDefine("Graphics position='16 10' size='200 150' alpha=200"); 
			TwAddVarRW(graphics_bar, "Polygon", TW_TYPE_BOOL8, &Packing_context()->toggle_polygons(), "");
			TwAddVarRW(graphics_bar, "Triangulation", TW_TYPE_BOOL8, &Packing_context()->toggle_triangulation(), "");
			TwAddVarRW(graphics_bar, "Voronoi", TW_TYPE_BOOL8, &Packing_context()->toggle_voronoi(), "");
			TwAddVarRW(graphics_bar, "CAT", TW_TYPE_BOOL8, &Packing_context()->toggle_cat(), "");
			TwAddVarRW(graphics_bar, "Box", TW_TYPE_BOOL8, &Packing_context()->toggle_bd_box(), "");
			TwAddVarRW(graphics_bar, "LNR", TW_TYPE_BOOL8,&Packing_context()->toggle_large_room(), "");
			TwAddButton(graphics_bar, "PDF", tw_print_pdf, NULL, "key=f");
			TwBar* function_bar = TwNewBar("Functions");
			TwDefine("Functions position='16 150' size='200 200' alpha=200");
			//TwAddVarRW(function_bar, "Iter Number.", TW_TYPE_INT32, Packing_context()->lloyd(), "min=1");
			TwAddButton(function_bar, "Lloyd", tw_lloyd, NULL, "key=l");
			TwAddButton(function_bar, "Shrink", tw_shrink, NULL, "key=r");
			TwAddVarRW(function_bar, "Enlarge", TW_TYPE_BOOL8, &Packing_context()->toggle_enlarge(), "");
			TwAddVarRW(function_bar, "Toggle Swap", TW_TYPE_BOOL8, &Packing_context()->toggle_swap(), "");
			//TwAddVarRW(function_bar, "Use Voronoi", TW_TYPE_BOOL8, &Packing_context()->toggle_use_voronoi(), "");
			TwAddVarRW(function_bar, "Iter", TW_TYPE_UINT32, &nb_iter, "min=1 max=50");
			TwAddVarRW(function_bar, "Highlight", TW_TYPE_INT32, &Packing_context()->set_highlight_no(), "");
			//TwAddVarRW(function_bar, "Max Rate", TW_TYPE_DOUBLE, &Packing_context()->set_max_rate(), "min=0.1 max=0.9");
			TwAddButton(function_bar, "Pack", tw_pack, NULL, "key=p");
			TwAddButton(function_bar, "SA Pack", tw_sa_pack, NULL, "key=P");
			TwAddButton(function_bar, "Swap", tw_swap, NULL, "key=s");
			TwAddButton(function_bar, "Optimize", tw_optimize, NULL, "key=o");
			toggle_skybox_CB() ;
            //glut_viewer_add_toggle('b', glut_viewer_is_enabled_ptr(GLUT_VIEWER_BACKGROUND), "switch Color/BW") ;
			*glut_viewer_is_enabled_ptr(GLUT_VIEWER_BACKGROUND) = GL_FALSE;
            //glut_viewer_add_toggle('v', &viewer_properties_->visible(), "viewer properties") ;
			viewer_properties_->visible() = GL_FALSE;
        }

    private:
        std::string polytope_filename ;
		unsigned int nb_iter;
		float coverage_rate;
    } ;


Packing::Packing_application* Packing_app() { return static_cast<Packing::Packing_application*>(Geex::GeexApp::instance()) ; }

void TW_CALL tw_lloyd(void *clientData) { Packing_app()->lloyd(); }
void TW_CALL tw_shrink(void *clientData) { Packing_app()->shrink(); }
void TW_CALL tw_pack(void *clientData) { Packing_app()->pack(); }
void TW_CALL tw_swap(void *clientData) { Packing_app()->swap(); }
void TW_CALL tw_sa_pack(void *clietData) { Packing_app()->sa_pack(); }
void TW_CALL tw_optimize(void *clientData)	{ Packing_app()->optimize(); }
void TW_CALL tw_print_pdf(void *clientData)
{
	std::cout << "Print to file MyFile.pdf ...";
	FILE *fp = fopen("MyFile.pdf", "wb");
	GLint buffsize = 0, state = GL2PS_OVERFLOW;
	GLint viewport[4];

	glGetIntegerv(GL_VIEWPORT, viewport);

	while( state == GL2PS_OVERFLOW ){ 
		buffsize += 1024*1024;//GL2PS_BSP_SORT 
		gl2psBeginPage ( "MyTitle", "MySoftware", viewport,
			GL2PS_PDF, GL2PS_BSP_SORT, GL2PS_SILENT |
			GL2PS_SIMPLE_LINE_OFFSET | GL2PS_NO_BLENDING |
			GL2PS_OCCLUSION_CULL | GL2PS_BEST_ROOT | 
			GL2PS_NO_PS3_SHADING | GL2PS_COMPRESS,
			GL_RGBA, 0, NULL, 2, 2, 2, buffsize,
			fp, "MyFile" );
		glut_viewer_redraw(); 
		state = gl2psEndPage();
	}

	fclose(fp);
	std::cout <<" End!" <<std::endl;
}
void tw_redraw() { Packing_app()->redraw(); }
//void TW_CALL tw_feature_set_callback(const void* fc, void *clientData) 
//{ 
//	spm_app()->feature_set_callback(*(double*)fc); 
//}
//void TW_CALL tw_feature_get_callback(void *value, void *clientData) 
//{ 
//	spm_app()->feature_get_callback((double*)value); 
//}
//void TW_CALL tw_curvature_get_callback(void *value, void *clientData)
//{
//	spm_app()->curvature_get_callback((double*)value);
//}
//void TW_CALL tw_curvature_set_callback(const void *value, void *clientData)
//{
//	spm_app()->curvature_set_callback(*(double*)value);
//}
}

int main(int argc, char** argv) 
{
//	try
//	{
		Geex::initialize();
		Packing::Packing_application app(argc, argv) ;
		app.main_loop() ;
		Geex::terminate() ;
//	}

//	catch(std::exception& e)
//	{
//		std::cerr<<"Exception thrown : "<<e.what()<<std::endl;
//#ifdef _DEBUG
//		system("pause");
//#endif
//	}
//	catch(...)
//	{
//		std::cerr<<"Unknown exception thrown.\n";
//#ifdef _DEBUG
//		system("pause");
//#endif
//	}

	return 0;
}
